﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;

//using LumiSoft.Net.Mime.vCard;

namespace library
{
    partial class p2pFile
    {
        class Packet
        {
            p2pFile File;

            byte[] Address;

            string Filename;

            internal bool Arrived
            {
                get
                {
                    lock (dataArrivedObjectLocker)
                        return Arrives > 0;
                }
            }

            int Arrives;

            internal int RequestSent;

            internal bool MayHaveLocalData = true;

            object dataArrivedObjectLocker = new object();

            internal Packet(p2pFile file, byte[] address, string filename = null)
            {
                File = file;

                Address = address;

                Filename = filename;
            }

            internal void VerifyDataArrived(byte[] address, byte[] data)
            {
                if (!Addresses.Equals(Address, address))
                    return;

                LocalPackets.OnPacketArrived -= VerifyDataArrived;

                ProcessDataArrived(data);
            }

            void ProcessDataArrived(byte[] data)
            {
                lock (dataArrivedObjectLocker)
                {


                    File.ReturnRatio = (File.ReturnRatio + (RequestSent / (double)Arrives + 1)) / 2;

                    if (Arrives != 0)
                        return;


                    var packetType = (PacketTypes)data[0];

                    switch (packetType)
                    {
                        case PacketTypes.Addresses:

                            List<byte[]> addresses = Addresses.ToAddresses(data.Skip(Parameters.packetHeaderSize));

                            foreach (byte[] addr in addresses)
                                File.AddPacket(addr, Filename);

                            break;

                        case PacketTypes.Directory:

                            Directory.CreateDirectory(File.Filename);

                            Dictionary<byte[], string> files =
                                Addresses.ToDirectories(data.Skip(Parameters.packetHeaderSize).ToArray());

                            foreach (byte[] addr in files.Keys)
                                File.AddPacket(addr, Path.Combine(File.Filename, files[addr]));

                            break;

                        case PacketTypes.Content:

                            var buffer = data.Skip(Parameters.packetHeaderSize).ToArray();

                            var offset = BitConverter.ToInt32(data, 1);

                            DelayedWrite.Add(Filename ?? File.Filename, buffer, offset);

                            break;

                        case PacketTypes.Post:

                            DelayedWrite.Add(Filename, data, 0);

                            break;
                    }

                    Arrives++;
                }
            }

            internal void Get()
            {
                byte[] data = null;

                if (MayHaveLocalData)
                {
                    data = LocalPackets.Get(Address);

                    if (data == null)
                        MayHaveLocalData = false;
                }

                if (data == null)
                {
                    var search = LocalPosts.Search(Address);

                    if (search.Any(x => x.HitValue.Content == FILE_TYPE_DOWNLOAD))
                    {
                        GetDirectory(search);

                        return;
                    }
                    else
                    {
                        Client.Stats.PresumedReceived.Add((int)(Parameters.packetSize / File.ReturnRatio));

                        Client.Stats.belowMaxReceivedEvent.WaitOne(File.MayHaveLocalData() ? 0 : Timeout.Infinite);

                        var sent = p2pRequest.Send(
                            address: Address,
                            wait: !File.MayHaveLocalData());

                        if (sent)
                        {
                            RequestSent++;

                            if (RequestSent == 1)
                                LocalPackets.OnPacketArrived += VerifyDataArrived;
                        }
                    }
                }
                else if (data != null)
                    ProcessDataArrived(data);
            }

            private void GetDirectory(ValueHits<p2pPost> search)
            {
                Directory.CreateDirectory(Filename);

                search.ForEach(x =>
                {
                    //var address = Utils.FromBase64String(x.HitValue.Content);

                    //if (address == null)
                    //    return;

                    var refs = LocalPosts.Search(x.HitValue.Address);

                    refs.ForEach(b =>
                    {
                        var address = Utils.FromBase64String(b.HitValue.Content);

                        if (address == null)
                            return;

                        var file = LocalPosts.Search(address);

                        var download = file.FirstOrDefault(a => a.HitValue.Content == FILE_TYPE_DOWNLOAD);

                        if (download != null)
                        {
                            var name = string.Empty;

                            var extension = string.Empty;

                            file.ForEach(z =>
                            {
                                var posts = LocalPosts.Search(z.HitValue.Address);

                                if (posts.Any(w => w.HitValue.Content == Utils.ToAddressSizeBase64String("Name")))
                                {
                                    name = z.HitValue.Content;

                                    return;
                                }

                                if (posts.Any(w => w.HitValue.Content == Utils.ToAddressSizeBase64String("Extension")))
                                {
                                    extension = z.HitValue.Content;

                                    return;
                                }

                            });

                            if(string.IsNullOrEmpty(File.SpecifFilename) || (name + extension).Equals(File.SpecifFilename))
                                File.AddPacket(download.HitValue.targetAddress, Path.Combine(File.Filename ?? Filename, name + extension));
                        }
                    });


                });

                Arrives++;
            }
        }
    }
}
